package com.fxu.framework.biz.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fxu.framework.biz.util.LoginUtil;
import com.fxu.framework.biz.util.TokenUtil;
import com.fxu.framework.core.base.Result;
import com.fxu.framework.core.base.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import static com.fxu.framework.core.base.Constant.*;

/**
 * Restfull 请求过滤
 * @author fangxu
 * @version 1.0.0
 */
@Slf4j
@WebFilter(filterName = "restFilter", urlPatterns = {"/api/*"})
public class RestFilter implements Filter {
	private final static Logger logger = LoggerFactory.getLogger(RestFilter.class);
	private List<String> pubs = CollUtil.toList();
	private List<String[]> specPubs = CollUtil.toList();

	/** 开放接口列表 */
	@Value("${pub-urls:}")
    private String pubUrls;

	@Value("${pub-domain:*}")
	private String pubDomain;
	
	@Override
	public void init(FilterConfig filterConfig) {
		if (!StrUtil.isEmpty(pubUrls)) {
			pubs = CollUtil.toList(pubUrls.split("\n"));
			for (String pub : pubs) {
				if (pub.contains("/*")) {
					specPubs.add(StrUtil.splitToArray(pub, "/"));
				}
			}
		}
		logger.debug("public-urls===>{}", pubs);
		logger.debug("public-domain===>{}", pubDomain);
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;
		res.setHeader("Access-Control-Max-Age", "3600");
        res.setHeader("Access-Control-Allow-Origin", pubDomain);
        res.setHeader("Access-Control-Allow-Methods", "OPTION, POST, GET"); // , DELETE, PUT
        res.setHeader("Access-Control-Allow-Headers", String.format("Content-Type, ignorecanceltoken, X-Requested-With, " +
						"cache-control, %s, %s, %s, %s, %s, %s, %s", HEADER_TOKEN, HEADER_AUTH, HEADER_CODE, HEADER_TYPE,
						HEADER_SIGN, HEADER_LANG, StrUtil.upperFirst(HEADER_LANG)));
		String path = req.getServletPath();
		String method = req.getMethod();
		String mtdAndPath = method.toLowerCase() + ":" + path;
		// 判断是否需要登录和权限
		boolean isPubUrl = pubs.contains(mtdAndPath) || has(mtdAndPath);
		//log.info("method={},isPubUrl={}", method, isPubUrl);
		if (!(method.equals("OPTIONS") || method.equals("HEAD") || isPubUrl)) {
			String token = TokenUtil.getToken(req);
			//logger.debug(method + ":" + path + ",token=" + token + ",isPubUrl=" + isPubUrl);
			// 登录超时使用401
			if (StrUtil.isEmpty(token)) {
				res.setStatus(401);
				res.setCharacterEncoding("UTF-8");
				res.setContentType("application/json; charset=utf-8");
				res.getWriter().write(JSONUtil.toJsonStr(Result.error(ResultCode.NEED_LOGIN)));
				return;
			}
			// 登录判断，如果登录延长登录时间
			if (!LoginUtil.isLogin(token, true)) {
				res.setStatus(401);
				res.setCharacterEncoding("UTF-8");
				res.setContentType("application/json; charset=utf-8");
				res.getWriter().write(JSONUtil.toJsonStr(Result.error(ResultCode.NEED_LOGIN)));
				return;
			}
			// 权限判断 => 带有侵入性, 采用注解@PermAuth("/test/index:list")方式
		}

		filterChain.doFilter(request, response);
		
	}

	// 兼容带有 * 匹配
	private boolean has(String url) {
		if (specPubs != null && specPubs.size() > 0) {
			return CollUtil.findOne(specPubs, v -> test(v, url)) != null;
		}
		return false;
	}

	// 兼容带有 * 匹配
	private static boolean test(String[] array, String test) {
		if (array == null || array.length <= 0 || StrUtil.isEmpty(test)) {
			return false;
		}
		String[] arr = StrUtil.splitToArray(test, "/");
		if (array.length != arr.length) {
			return false;
		}
		for (int i = 0; i < array.length; i++) {
			if ((i > 0 && i < array.length - 1 && StrUtil.isEmpty(arr[i])) || "*".equals(arr[i])) {
				return false;
			}
			if (!array[i].equals(arr[i]) && !"*".equals(array[i])) {
				return false;
			}
		}
		return true;
	}

	@Override
	public void destroy() {
		this.pubs = null;
		this.pubUrls = null;
		this.specPubs = null;
	}
}